﻿using System;
using System.Collections.Generic;
using Unity.Mathematics;
using UnityEngine;

#if UNITY_EDITOR
using UnityEditor;
using UnityEngine.Rendering;

#endif

namespace Mars.GpuInstance
{
    [RequireComponent(typeof(Animator))]
    public class GPUAnimatorInstancerForBake : MonoBehaviour
    {
#if UNITY_EDITOR
        
        public GPUAnimationData animationData;
        public string anchorInfo;
        public int frameRate = 30;
        public ShadowLod animatorShadowLod = ShadowLod.AUTO_SELECT;
        [Header("名字转的hashcode")]
        public int prefabName;
        [Header("计算包围盒时把包围盒扩大一点，避免看到在边缘的时候模型被裁剪掉")]
        public Vector3 boxExtents = Vector3.zero;
        public List<SkinMeshLodDataForBake> LodDatas = new List<SkinMeshLodDataForBake>();
        [Header("是否需要烘焙动态挂点")]
        public bool needBakeAnchorInfo = true;

        private void OnValidate()
        {
            if (Application.isBatchMode)
            {
                return;
            }
            for (int i = 0; i < LodDatas.Count; i++)
            {
                var lodData = LodDatas[i];
                if (lodData.transform == null)
                {
                    continue;
                }
                var meshRenderers = lodData.transform.gameObject.GetComponentsInChildren<Renderer>();
                if (lodData.renderDatas == null)
                {
                    lodData.renderDatas = new List<SkinMeshRenderDataForBake>();
                }
                else
                {
                    lodData.renderDatas.Clear();
                }

                foreach (var msr in meshRenderers)
                {
                    if (msr.sharedMaterial == null)
                    {
                        continue;
                    }
                    msr.sharedMaterial.enableInstancing = true;
                    var transformOffset = msr.localToWorldMatrix;
                    lodData.renderDatas.Add(new SkinMeshRenderDataForBake
                    {
                        meshRenderer = msr,
                        transformOffset = transformOffset
                    });
                }
            }
            EditorUtility.SetDirty(gameObject);
        }
        
        public void RefreshName()
        {
            if (UnityEditor.PrefabUtility.IsPartOfPrefabAsset(gameObject))
            {
                var str = InstancerEditorUtil.GetPrefabAssetName(gameObject);
                prefabName = InstancerUtil.GetStringHashCode(str);
            }
            else
            {
                var str = gameObject.name;
                prefabName = InstancerUtil.GetStringHashCode(str);
            }
            
            var meshRenders = gameObject.GetComponentsInChildren<MeshRenderer>();
            foreach (var meshRender in meshRenders)
            {
                var meshfilter = meshRender.gameObject.GetComponentInChildren<MeshFilter>();
                if (meshfilter == null)
                {
                    continue;
                }
                
                foreach (var sharedMaterial in meshRender.sharedMaterials)
                {
                    if (sharedMaterial != null && !sharedMaterial.enableInstancing)
                    {
                        sharedMaterial.enableInstancing = true;
                        EditorUtility.SetDirty(sharedMaterial);
                    }
                }
            }

            var skinRenders = gameObject.GetComponentsInChildren<SkinnedMeshRenderer>();
            foreach (var skinRender in skinRenders)
            {
                foreach (var sharedMaterial in skinRender.sharedMaterials)
                {
                    
                    if (sharedMaterial != null && !sharedMaterial.enableInstancing)
                    {
                        sharedMaterial.enableInstancing = true;
                        EditorUtility.SetDirty(sharedMaterial);
                    }
                }
            }

            for (int i = 0; i < LodDatas.Count; i++)
            {
                var lods  = LodDatas[i];
                for (int j = 0; j < lods.renderDatas.Count; j++)
                {
                    var renderData = lods.renderDatas[j];
                    if (renderData.meshRenderer == null)
                    {
                        Debug.LogError($"[{nameof(GPUAnimatorInstancerForBake)}] RefreshName(). meshRenderer is null. gameObject = {gameObject.name}, lod = {i}, rendererIndex = {j}");
                    }
                    else
                    {
                        renderData.transformOffset = renderData.meshRenderer.localToWorldMatrix;
                    }
                }
            }
            
            
            EditorUtility.SetDirty(gameObject);
        }
        
#endif
    }
    
    [Serializable]
    public class SkinMeshLodDataForBake : IComparable<SkinMeshLodDataForBake>
    {
        public int lodDis;
        public Transform transform;
        public List<SkinMeshRenderDataForBake> renderDatas;

        public int CompareTo(SkinMeshLodDataForBake obj)
        {
            if (lodDis == obj.lodDis)
            {
                return 0;
            }
            else if (lodDis > obj.lodDis)
            {
                return 1;
            }

            return -1;
        }
    }

    [Serializable]
    public class SkinMeshRenderDataForBake
    {
        public Renderer meshRenderer;
        [HideInInspector] 
        public Matrix4x4 transformOffset;
    }
}